package queries;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction;

import graph.Graph;
import jTree.JunctionTree;
import jTree.Node;
import jTree.NodeSet;


public class MapQuery {

	public int largestCliqueIndex;
	public int MapValues[];
	JunctionTree tree = null;
	
	public MapQuery(JunctionTree jt){
		
		tree = copyJunctionTree(jt);
		largestCliqueIndex = findLargestCliqueIndex(tree);			// Finding the largest clique
		
		System.out.println("CliqueIndex for Largest clique found is "+largestCliqueIndex);
		createBranchDirections(largestCliqueIndex,-1);					// Creating Directions in tree with respect to the branches.
		
		MapValues = new int[(int)Graph.numNodes];					// Place to store the final Map Values
		
		
	}

		public JunctionTree copyJunctionTree(JunctionTree t){
			JunctionTree newTree = new JunctionTree();
			newTree.numberOfNodes = t.numberOfNodes;
			newTree.treeNodes = new ArrayList<NodeSet>();
		
			for(NodeSet clq : t.treeNodes){
				NodeSet temp;
				temp = copyNodeSet(clq);	
				newTree.treeNodes.add(temp);
			}
		
			return newTree;
		}
	
	public NodeSet copyNodeSet(NodeSet n){
		
		NodeSet temp = new NodeSet();
		
		temp.list = new ArrayList<Node>();
		
		for(Node nt : n.list){
			Node t = copyNode(nt);
			temp.list.add(t);
		}
	
		temp.branch = new ArrayList<Integer>();
		
		for(int i : n.branch)
			temp.branch.add(i);
	
		temp.parent = n.parent;
		
		temp.offset = new int[n.offset.length];
		for(int i=0; i<n.offset.length; i++){
			temp.offset[i] = n.offset[i];
		}
	
		temp.table = new mapAssign[n.table.length];
		for(int i=0; i<n.table.length; i++){
			temp.table[i] = copymapAssign(n.table[i]);
		}
		
		return temp;
	}

	public mapAssign copymapAssign(mapAssign t){
		mapAssign temp = new mapAssign();
	
		temp.value = t.value;
		
		temp.nodeIndex = new ArrayList<Integer>();
		temp.assValue = new ArrayList<Integer>();
		
		
		for(int i: t.nodeIndex)
			temp.nodeIndex.add(i);
		
		for(int i: t.assValue)
			temp.assValue.add(i);
		
		return temp;

	}
	
	public Node copyNode(Node n){
		
		Node newNode = new Node((int)n.index);
		
		newNode.neighbourCount = n.neighbourCount;
	
		return newNode;
	}
	
	
	public int findLargestCliqueIndex(JunctionTree jt){
			
			int maxSize = -1;
			int maxIndex = 0;
			
			for(NodeSet n : jt.treeNodes ){
				if(n.list.size() > maxSize){
					maxSize = n.list.size();
					maxIndex = jt.treeNodes.indexOf(n);
					
				}
			}
			
			return maxIndex;
	}

	public void createBranchDirections(int currentNodeIndex, int parent){
		
			tree.treeNodes.get(currentNodeIndex).parent = parent;
			
			System.out.println("clique Node visited "+currentNodeIndex);
			
			for(int n : tree.treeNodes.get(currentNodeIndex).branch){
				if(n != parent){
					createBranchDirections(n,currentNodeIndex);
				}
			}	
	}

	public void getMap(int root, mapAssign tab[], List<Node>separator, int sepOffsetValues[]){
		
		 mapAssign table[];			// Table that will contain the messages returned from the branches to the present node	
		
		 
	//	 System.out.println("Inside GetMaP for clique "+root);
		 
		 
		 	 
		 if(tree.treeNodes.get(root).branch.size() > 0){
				for(int i : tree.treeNodes.get(root).branch){
						if(i != tree.treeNodes.get(root).parent){
								
								List<Node> common = new ArrayList<Node>(tree.treeNodes.get(i).list);
								//common.retainAll(tree.treeNodes.get(root).list);						// Finding out the separator
							
								intersection(common,tree.treeNodes.get(root).list);
								
								
								int cumValue[] = new int[common.size()];
								List<Node> s1List = new ArrayList<Node>(common);  			
								
								System.out.println("Length of separator of clique "+root+" with clique "+i+" is "+common.size());
								System.out.println("The elements common are:");
								
								for(Node n : s1List)
									System.out.print(n.index+" ");
								System.out.println();
								
								cumValue[cumValue.length - 1 ] = 1;
								for(int j = s1List.size() - 2; j>=0; j--){
									cumValue[j] = cumValue[j+1] * (int)Graph.maxValues[(int)s1List.get(j).index]; 
								} 	
								table = new mapAssign[(int)cumValue[0] * (int)Graph.maxValues[(int)s1List.get(0).index]];	
							
								for(int j = 0; j<table.length; j++){
										table[j] = new mapAssign();
								}
								
								getMap(i,table,s1List,cumValue);
								
								System.out.println("Inside getMap Back to clique "+root);
								
								for(int j = 0; j<tree.treeNodes.get(root).table.length; j++){
										
										int netOffset = 0;
										for(int k = 0; k<s1List.size(); k++ ){
											
											int indexInClique = findIndex(tree.treeNodes.get(root).list, s1List.get(k));  //tree.treeNodes.get(root).list.indexOf(s1List.get(k));
											
											if(indexInClique != -1){
											
												int variableValue = ( j/(tree.treeNodes.get(root).offset[indexInClique]) ) % (int)(Graph.maxValues[(int)s1List.get(k).index]);
												netOffset = (int) (netOffset + cumValue[k] * variableValue);		
											
											}
											else{
												System.out.println("Index Error!!!");
											}
										}
										
										tree.treeNodes.get(root).table[j].value = tree.treeNodes.get(root).table[j].value * table[netOffset].value;
										
										//System.out.println("Table recieved in clique "+root+" has MapNode size "+table[netOffset].nodeIndex.size());
										// Adding Map Values
										for(int k = 0; k<table[netOffset].nodeIndex.size(); k++){
											tree.treeNodes.get(root).table[j].nodeIndex.add(table[netOffset].nodeIndex.get(k));
											tree.treeNodes.get(root).table[j].assValue.add(table[netOffset].assValue.get(k));
										}	
													
									//	System.out.println("Sucessfully added "+tree.treeNodes.get(root).table[j].nodeIndex.size()+" values");
								
								
								}
							
						}
				}
						
		 }		
		
		 if(tree.treeNodes.get(root).parent == -1){
			 	// Find the final maximum assignment
			 	double maxValue = -999;
			 	int maxOffset = 0;
			 	double sum = 0;
			 	for(int i = 0; i<tree.treeNodes.get(root).table.length; i++){
			 		if(tree.treeNodes.get(root).table[i].value > maxValue ){
			 			maxValue = tree.treeNodes.get(root).table[i].value;
			 			maxOffset = i;
			 		}
					sum = sum + tree.treeNodes.get(root).table[i].value;
			 	
			 	}
		 
			 	int variableValue;
			 	for(int i = 0; i<tree.treeNodes.get(root).list.size(); i++){
			 		
			 		variableValue = ( maxOffset / tree.treeNodes.get(root).offset[i] ) % (int)(Graph.maxValues[(int)(tree.treeNodes.get(root).list.get(i).index)]);
			 		tree.treeNodes.get(root).table[maxOffset].nodeIndex.add((int)tree.treeNodes.get(root).list.get(i).index);
			 		tree.treeNodes.get(root).table[maxOffset].assValue.add(variableValue);
			 	}
		 
			 	System.out.println("The MAP VALUES ARE !!!");
			 	for(int i = 0; i<tree.treeNodes.get(root).table[maxOffset].nodeIndex.size(); i++){
			 			MapValues[i] = tree.treeNodes.get(root).table[maxOffset].assValue.get(i);
			 			System.out.println("Node "+tree.treeNodes.get(root).table[maxOffset].nodeIndex.get(i)+": "+MapValues[i]);
			 	}
			 	
				System.out.println(maxValue);

			 	return;
		 }
		 
		 System.out.println("Still Inside getMap with clique "+root);
		 
		 double sum_non_parent = 0;
		 
		 for(int i = 0; i<tree.treeNodes.get(root).table.length; i++){
				sum_non_parent = sum_non_parent + tree.treeNodes.get(root).table[i].value;
		 }	 
		 
		// for(int i = 0; i<tree.treeNodes.get(root).table.length; i++){
			//	tree.treeNodes.get(root).table[i].value = (double)tree.treeNodes.get(root).table[i].value / sum_non_parent;
		// }
		 
		 List <Node> onlyRoot = new ArrayList<Node>(tree.treeNodes.get(root).list);
		 //onlyRoot.removeAll(separator);
		 
		 setMinus(onlyRoot,separator);
		 
		 System.out.println("Only Root Size "+onlyRoot+" with elements");
		 
		 for(Node x : onlyRoot)
			 System.out.print(x.index+" ");
		 System.out.println();
		 
		 
		 int sepVariableValues[] = new int[separator.size()];
		 int returnedOffset;
		 int netOffset = 0;

	/*	 System.out.println("Length of message passed by clique "+root+" is "+separator.size());
		 System.out.println("Length of message matrix passed by clique "+root+" is "+tab.length);
		 System.out.println("Length of list of clique "+root+" is "+tree.treeNodes.get(root).list.size());
	*/	 
		 
		 
	//	 System.out.println("The message sent by clique "+root+" to "+tree.treeNodes.get(root).parent);
		 
		 double sum = 0;
		 
		 
		 for(int i = 0; i<tab.length; i++){
		 
			 	netOffset = 0;
			 	for(int j = 0; j<separator.size(); j++){
			 		sepVariableValues[j] = ( i/sepOffsetValues[j] ) % (int)Graph.maxValues[(int)separator.get(j).index];
			 	
			 		//System.out.println("Index sep pos 0:"+separator.get(j).index+" neighbourcount :"+separator.get(j).neighbourCount);
			 		//System.out.println("Index clique pos 1:"+tree.treeNodes.get(root).list.get(1).index+" neighbourcount :"+tree.treeNodes.get(root).list.get(1).neighbourCount);
			 		
			 		
			 		//System.out.println("Index "+j+" in message is Index "+tree.treeNodes.get(root).list.indexOf(separator.get(j))+" in clique "+root);
			 		
			 		netOffset = netOffset + sepVariableValues[j] * tree.treeNodes.get(root).offset[findIndex(tree.treeNodes.get(root).list,separator.get(j))];   //tree.treeNodes.get(root).offset[tree.treeNodes.get(root).list.indexOf(separator.get(j))];
			 	
			 	}	
			 	
			 //	System.out.println("Offset from the common part "+netOffset);
			 	returnedOffset = reccMessage(root,onlyRoot,0,sepVariableValues,netOffset);
			 	tab[i].value = tree.treeNodes.get(root).table[returnedOffset].value;
			 	sum = sum + tab[i].value; 
			 	for(int k = 0; k<tree.treeNodes.get(root).table[returnedOffset].nodeIndex.size(); k++){
			 		tab[i].nodeIndex.add(tree.treeNodes.get(root).table[returnedOffset].nodeIndex.get(k));
			 		tab[i].assValue.add(tree.treeNodes.get(root).table[returnedOffset].assValue.get(k));
			 	}
			 	
			 	for(int j = 0; j<onlyRoot.size(); j++){
			 		tab[i].nodeIndex.add((int)onlyRoot.get(j).index);
			 		
			 		int indexInClique = findIndex(tree.treeNodes.get(root).list,onlyRoot.get(j));   //tree.treeNodes.get(root).list.indexOf(onlyRoot.get(j));
			 		int variableValue = ( returnedOffset / tree.treeNodes.get(root).offset[indexInClique] ) % (int)(Graph.maxValues[(int)onlyRoot.get(j).index]);
			 		tab[i].assValue.add(variableValue);
			 		
			 		
			 	}
			 //	System.out.println("MaxValue: "+tab[i].value);
			 	for(int k = 0; k<tab[i].nodeIndex.size(); k++){
		 	 
		 	 		//System.out.println("For node "+tab[i].nodeIndex.get(k)+" with value "+tab[i].assValue.get(k));
		 		}
		 }
		 
		 System.out.println("Sum for tab "+sum);
		 
		// for(int i = 0; i<tab.length; i++){
		//	tab[i].value = tab[i].value;
	    // }	 
		
	}

	public int reccMessage(int root, List<Node>onlyRoot, int index, int sepVariableValues[], int netOffset){ 
		
		int maxOffset = 0;
		if(index < onlyRoot.size()-1){
				
								
				int offsetInClique = findIndex(tree.treeNodes.get(root).list,onlyRoot.get(index));		//tree.treeNodes.get(root).list.indexOf(onlyRoot.get(index));	
				int tempOffset; 
				maxOffset = netOffset; 
				for(int i = 0; i<Graph.maxValues[(int)onlyRoot.get(index).index]; i++){
					
					tempOffset = netOffset;
					tempOffset = tempOffset + (i * tree.treeNodes.get(root).offset[offsetInClique]);
					int returnOffset = reccMessage(root,onlyRoot,index+1,sepVariableValues,tempOffset);
					
					if(tree.treeNodes.get(root).table[returnOffset].value > tree.treeNodes.get(root).table[maxOffset].value){
						maxOffset = returnOffset;
					}
				}
		}
		
		if(index == onlyRoot.size() - 1){
			
			int offsetInClique = findIndex(tree.treeNodes.get(root).list,onlyRoot.get(index));	//tree.treeNodes.get(root).list.indexOf(onlyRoot.get(index));
			int tempOffset;
			
			maxOffset = netOffset;
			for(int i = 0; i<Graph.maxValues[(int)onlyRoot.get(index).index]; i++){
				tempOffset = netOffset;
				tempOffset = tempOffset + (i * tree.treeNodes.get(root).offset[offsetInClique]);
			
				
				
				if(tree.treeNodes.get(root).table[tempOffset].value > tree.treeNodes.get(root).table[maxOffset].value){
					maxOffset = tempOffset;
				}
		
			}
			
			
		}	
		
	//	System.out.println("Final Offset "+maxOffset);
		return maxOffset;
	} 	


	public void intersection(List<Node>s1, ArrayList<Node>s2){
		
		boolean found = false;
		
		for(int i = 0; i<s1.size(); i++){
			found = false;
			for(int j = 0; j<s2.size(); j++){
				if(s1.get(i).index == s2.get(j).index){
					found = true;
					break;
				}
			}
			
			if(!found){
				s1.remove(i);
				i--;
			}	
		}
	
	}

	public void setMinus(List<Node>s1, List<Node>s2){
		
		boolean found = false;
		
		for(int i = 0; i<s1.size(); i++){
			
			found = false;
			for(int j = 0; j<s2.size(); j++ ){
				if(s1.get(i).index == s2.get(j).index){
					found = true;
					break;
				}
			}
		
			if(found){
				s1.remove(i);
				i--;
			}	
		}
			
	
	}

	public int findIndex(ArrayList<Node>s1, Node n){
		
		for(int i = 0; i<s1.size(); i++){
			if(n.index == s1.get(i).index)
				return i;
		}
		
		return -1;
	}

}



